package com.gitee.wsl.mathematics.function.noise.d3

import com.gitee.wsl.ext.number.wrap
import com.gitee.wsl.mathematics.function.noise.Noise3d
import com.gitee.wsl.mathematics.vector.vec3.MutableVec3f
import kotlin.math.min
import kotlin.math.sqrt
import kotlin.random.Random

class GridWorleyNoise3d(val gridSizeX: Int, val gridSizeY: Int, val gridSizeZ: Int, seed: Int = 19937) :
    Noise3d {

    private val grid = Array(gridSizeX * gridSizeY * gridSizeZ) { MutableVec3f() }

    init {
        val rand = Random(seed)

        for (z in 0 until gridSizeZ) {
            for (y in 0 until gridSizeY) {
                for (x in 0 until gridSizeX) {
                    grid(x, y, z).set(rand.nextFloat(), rand.nextFloat(), rand.nextFloat())
                }
            }
        }
    }

    private fun grid(x: Int, y: Int, z: Int): MutableVec3f {
        val xw = x.wrap(0, gridSizeX)
        val yw = y.wrap(0, gridSizeY)
        val zw = z.wrap(0, gridSizeZ)
        return grid[zw * (gridSizeX * gridSizeY) + yw * gridSizeX + xw]
    }

    override fun eval(x: Float, y: Float, z: Float): Float {
        val px = x * gridSizeX
        val py = y * gridSizeY
        val pz = z * gridSizeZ
        val ix = px.toInt()
        val iy = py.toInt()
        val iz = pz.toInt()

        var d = MAX_D
        for (gz in iz-1 .. iz+1) {
            for (gy in iy-1 .. iy+1) {
                for (gx in ix-1 .. ix+1) {
                    val v = grid(gx, gy, gz)
                    val dx = px - (v.x + gx)
                    val dy = py - (v.y + gy)
                    val dz = pz - (v.z + gz)
                    d = min(d, sqrt(dx * dx + dy * dy + dz * dz))
                }
            }
        }
        return d / MAX_D
    }

    companion object {
        private val MAX_D = sqrt(3f)
    }
}